#include <stdio.h>
#include <ctype.h>
#include"red_black_tree.h"
#include "pcb.h"
#include "sim.h"
#include "sched1.h"
#include "doubleL.h"


/*store node's pointers,can increase if process pid greater than containnum*/
rb_red_blk_node** pointers;
/*red-black tree priority queue*/
rb_red_blk_tree* tree;

unsigned CONTAINNUM=10000000;

static void Remove(PCB *p);
/*just for init red black tree start*/
static void IntDest(void* a);
static int IntComp(const void* a,const void* b);
static void IntPrint(const void* a);
static void InfoPrint(void* a);
static void InfoDest(void *a);
/* end */
static PCB* nextToRun();
static void Remove(PCB *p);

  
/*directly get from simple code*/  
static void IntDest(void* a) {
  free((int*)a);
}

/*directly get from simple code*/
static int IntComp(const void* a,const void* b) {
  if( *(int*)a > *(int*)b) return(1);
  if( *(int*)a < *(int*)b) return(-1);
  return(0);
}

/*directly get from simple code*/
static void IntPrint(const void* a) {
  printf("%i",*(int*)a);
}

/*directly get from simple code*/
static void InfoPrint(void* a) {
  ;
}
/*directly get from simple code*/
static void InfoDest(void *a){
  ;
}


/*find and delete the node which pid =p's pid'*/
void InorderTree(rb_red_blk_tree* tree, rb_red_blk_node* x,PCB *p) 
{
    PCB* tmp;
   
  if (x != tree->nil) {
    InorderTree(tree,x->left,p);
    tmp=x->info;
    if(getPid(tmp)==getPid(p))
    {
        RBDelete(tree,x);
        return;
    }
    InorderTree(tree,x->right,p);
  }
}


/*init scheduler2*/
void initSched2()
{

	tree=RBTreeCreate(IntComp,IntDest,InfoDest,IntPrint,InfoPrint);
    if(NULL==tree)
	{
		fprintf(stderr, "Fail to init priority queue\n");
		exit(EXIT_FAILURE);
	}
	pointers=(rb_red_blk_node**)malloc(CONTAINNUM*sizeof(rb_red_blk_node*));
    if(NULL==pointers)
	{
		fprintf(stderr, "Fail to malloc memory\n");
		exit(EXIT_FAILURE);
	}
}

/*call when whole sim finish*/
void freePointers()
{
    int i=0;
    for(i=0;i<CONTAINNUM;i++)
    {
        free(pointers[i]);
    }
}

/*select the next process to run */
static PCB* nextToRun()
{
    PCB* tmp=NULL;
    rb_red_blk_node* tmpNode;
    if(tree->nil==tree->root->left)/* */
    {
        return NULL;
    }
    else
    {	
        tmpNode=tree->root;
        while(tmpNode->left!=tree->nil)
        {
            tmpNode=tmpNode->left;
        }
        tmp=(PCB*)tmpNode->info;
        
        /*don't remove running node from list - david 
		*/
		setTimeSlice(tmp,getCpuTime(tmp));
        return tmp;
    }
}


/*remove node contain p*/
static void Remove(PCB *p)
{
    /*InorderTree(tree,tree->root->left,p);*/
    unsigned pid=getPid(p);
    if(pointers[pid]!=NULL)
    {
    RBDelete(tree,pointers[pid]);
    pointers[pid]=NULL;
    
    }
    
}

/* scheduler returns program pointer of 
 * program to  run  or NULL for no change to 
 * currently running program.
 */

PCB* handleEvent2(Event e, PCB* p)
{
	PCB* tmp;
	int* newInt;
	rb_red_blk_node* tmpNode;
	rb_red_blk_node** ptr;
	switch(e)
	{
		
		case TERMPROC:
		    Remove(p);
		case IOSTART:
		    Remove(p);
			/*
			remove from queue here,
			* might not be the first element
			* but should be close to the head
			*/
			break;
        case TERMSLICE:
			/* don't need to'*/
        	break;
		case IOCOMPLETE:
		    if(!isProcessComplete(p))
		    {
		      newInt=(int*) malloc(sizeof(int));/*use in this way,learn from example*/
	         *newInt=(getCpuTime(p)-getCompletedTime(p));
	         tmpNode=RBTreeInsert(tree,newInt,(PCB*)p);/*add to priority queue*/
	         if(getPid(p)>CONTAINNUM-1)/*allocate new memory*/
	         {
	            CONTAINNUM=CONTAINNUM*2;
	            ptr=(rb_red_blk_node**)realloc(pointers,CONTAINNUM*sizeof(rb_red_blk_node*));
	            pointers=ptr;
	         }
	         pointers[getPid(p)]=tmpNode;
		    }

			/*return the next program to run*/
		    break;
		case PROCESSSTART:
            newInt=(int*) malloc(sizeof(int));
	        *newInt=(getCpuTime(p)-getCompletedTime(p));
	        tmpNode=RBTreeInsert(tree,newInt,(PCB*)p);/*add to priority queue*/
	        if(getPid(p)>CONTAINNUM-1)
	         {
	            CONTAINNUM=CONTAINNUM*2;
	            ptr=(rb_red_blk_node**)realloc(pointers,CONTAINNUM*sizeof(rb_red_blk_node*));
	            pointers=ptr;
	         }
	        pointers[getPid(p)]=tmpNode;
		    
			/* debug 
			list_sort_simple(stcf_queue);	
			for(i=1;i<=list_length(stcf_queue);i++){
				tmp = (PCB*)list_get_element(stcf_queue,i);
				printf("%s(PID:%u):%u\n", 
					tmp->name, tmp->pid, tmp->cpu_time-tmp->completed_cpu_time);
			}
			 end debug */
			break;
		default:
			fprintf(stderr, "Wrong Event\n");
			exit(EXIT_FAILURE);

	}
    tmp=nextToRun();
    return tmp;
}


